package com.jingli.pile.util;
import org.apache.commons.lang3.StringUtils;

import java.nio.ByteBuffer;
import java.security.*;
public class HMacMd5SignSpi extends SignatureSpi{
    private static final byte opad = 0x5c;
    private static final byte ipad = 0x36;
    private static final byte KEN_LEN = 64;

    private Key key;

    private ByteBuffer buf = ByteBuffer.allocate(KEN_LEN);

    @Override
    protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
        this.key = new HMacMd5KeyFactory().engineTranslateKey(publicKey);
    }

    @Override
    protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
        this.key = new HMacMd5KeyFactory().engineTranslateKey(privateKey);
    }

    @Override
    protected void engineUpdate(byte b) {
        buf.rewind();
        buf.put(b);
        buf.flip();
    }

    @Override
    protected void engineUpdate(byte[] b, int off, int len) {
        if(buf.capacity()<len){
            buf = ByteBuffer.allocate(len);
        }
        buf.rewind();
        buf.put(b, off, len);
        buf.flip();
    }

    @Override
    protected byte[] engineSign() {
        return calcSigin();
    }

    private byte[] calcSigin() {

        byte[] data = new byte[buf.limit()];
        byte[] k = key.getEncoded();
        int keyLen = k.length;

        byte[] str = new byte[keyLen];

        buf.rewind();
        buf.get(data);


        byte[] data1 = new byte[keyLen+ data.length];
        System.arraycopy(generateIstr(k, str), 0, data1, 0, keyLen);
        System.arraycopy(data, 0, data1, keyLen, data.length);
        byte[] md5One = Md5Utils.md5Bytes(data1);

        byte[] data2 = new byte[keyLen+md5One.length];
        System.arraycopy(generateOstr(k, str), 0, data2, 0, keyLen);
        System.arraycopy(md5One, 0, data2, keyLen, md5One.length);

        return Md5Utils.md5Bytes(data2);
    }

    @Override
    protected boolean engineVerify(byte[] inSign) {

        byte[] calcSign = this.calcSigin();

        String hexCalcSign = HexUtils.bytes2HexString(calcSign);
        String hexInSign = HexUtils.bytes2HexString(inSign);

        return StringUtils.equals(hexCalcSign, hexInSign);
    }

    @Override
    protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
        //skip
    }

    @Override
    protected Object engineGetParameter(String param) throws InvalidParameterException {
        return null;
    }


    /**
     * 生成istr数据.
     * @param key 签名密钥.
     *            <p>64字节长.</p>
     * @param out 输出数组.
     * @return 结果.
     */
    public static byte[] generateIstr(byte[] key, byte[] out){

        for(int i = 0; i< KEN_LEN; i++){
            out[i]=(byte)(key[i]^ipad);
        }
        return out;
    }

    /**
     * 生成ostr数据.
     * @param key 签名密钥.
     *            <p>64字节长.</p>
     * @param out 输出数组.
     * @return 结果.
     */
    private static byte[] generateOstr(byte[] key, byte[] out) {
        for(int i = 0; i< KEN_LEN; i++){
            out[i]=(byte)(key[i]^opad);
        }
        return out;
    }
}
